#include "global.h"
#include "schedule.h"

#include <QLabel>
#include <QHash>
#include <QRandomGenerator>

extern int MINI_WORKDAY_DAY;
extern int MINI_WORKDAY_NIGHT;
extern int MINI_HOLIDAY_DAY;
extern int MINI_HOLIDAY_NIGHT;
extern int MINI_SPECIALDAY_DAY;
extern int MINI_SPECIALDAY_NIGHT;

Schedule::Schedule(QWidget *parent) : QWidget(parent)
{

}

Schedule::Schedule(QWidget *parent, QStackedWidget *pStackWidget)
{
    setParent(parent);
    this->myParent = parent;
    this->pStackWidget = pStackWidget;
    this->tableLayout = NULL;
    this->btnLayout = NULL;
    this->widgetLayout = NULL;
    this->pTable = NULL;
    this->pExport = NULL;
    pDataInMonth = (DataInMonth *)malloc(sizeof(DataInMonth));
    clearData();

    pExport = new ExportTable(this);
    connect(this, &Schedule::signal_exportTableBtn_triggered, pExport, &ExportTable::slot_exportTable_triggered);

}

Schedule::~Schedule()
{
    if(NULL != pDataInMonth){
        free(pDataInMonth);
        pDataInMonth = NULL;
    }
}

void Schedule::clearData()
{
    specialColumnStart = 0;
    specialColumnCount = 0;
    specialRowStart = 0;
    specialRowCount = 0;
    memset(pDataInMonth, 0, sizeof(DataInMonth));

    if(NULL != tableLayout){
        deleteBoxLayout(tableLayout);
        delete  tableLayout;
        tableLayout = NULL;
    }

    if(NULL != btnLayout){
        deleteBoxLayout(btnLayout);
        delete  btnLayout;
        btnLayout = NULL;
    }

    if(NULL != widgetLayout){
        deleteBoxLayout(widgetLayout);
        delete  widgetLayout;
        widgetLayout = NULL;
    }

    if(NULL != pTable){
        delete  pTable;
        pTable = NULL;
    }

    for(int i = 0; i < memberList.count(); i++){
        if(NULL == memberList.at(i))    continue;
        delete memberList.at(i);
    }

    for(int i = 0; i < dayList.count(); i++){
        if(NULL == dayList.at(i))    continue;
        delete dayList.at(i);
    }

    memberNameList.clear();
    dayStringList.clear();
    memberList.clear();
    dayList.clear();

    return;
}

void Schedule::initScheduleTable()
{
    clearData();

    // 创建表格
    if(NULL != pTable)  delete pTable;

    QStringList strHorizontalList;
    QStringList strVerticalList;
    pTable = new MyScheduleTable(myParent);

    getMemberStringlist(MEMBERLIST);
    strHorizontalList = memberNameList + QStringList({"白班人数", "夜班人数", "总人数", "备注"});
    pTable->createHorizontalHeader(strHorizontalList);

    getDayStringlist(MYDAYLIST);
    strVerticalList = dayStringList + QStringList({"个人白班小计", "个人夜班小计", "个人班时总计"});
    pTable->createVerticalHeader(strVerticalList);

    // 设置样式
    pTable->setScheduleHorizontalHeaderStyle(QFont("song", 14), TABLEWIDGET_HORIZONTALHEADER_STYLE, true);
    pTable->setVerticalHeaderStyle(QFont("song", 14), TABLEWIDGET_VERTICALHEADER_STYLE, true);
    pTable->setScheduleTableWidgetStyle();

    // 创建单元格
    pTable->createItemAndSetStyle(specialRowStart, specialRowCount, specialColumnStart, specialColumnCount);

    // 创建右键菜单
    pTable->createActions(MYDAYLIST.count(), MEMBERLIST.count());
    connect(pTable, &MyScheduleTable::signal_changeScheduleWorkAction_triggered, this, &Schedule::slot_changeScheduleWorkAction_triggered);

    // 设置布局
    if(NULL != tableLayout){
        deleteBoxLayout(tableLayout);
        delete tableLayout;
    }
    tableLayout = new QHBoxLayout;
    tableLayout->addWidget(pTable);

    return;
}

void Schedule::getMemberStringlist(const QList<MemberClass*> memberList)
{
    MemberClass* pTmp = NULL;

    memberNameList.clear();
    for(int i = 0; i < memberList.count(); i++){
        pTmp = memberList.at(i);
        if(NULL == pTmp)    continue;
        memberNameList.append(pTmp->getMemberName());
    }

    specialColumnStart = memberList.count();
    specialColumnCount = 4;
    return;
}

void Schedule::getDayStringlist(const QList<myDay *> mydayList)
{
    myDay * pTmp = mydayList.at(0);
    if(NULL == pTmp){
        QLOG_WARN() <<  "NULL == pTmp";
        return;
    }

    dayStringList.clear();
    for(int i = 0; i < mydayList.count(); i++)
        dayStringList.append(QString::number(pTmp->getMonth()) + "月" + QString::number(i + 1) + "日");

    specialRowStart = mydayList.count();
    specialRowCount = 3;

    return;
}

void Schedule::initSchedulePushButton()
{
    QPushButton *pBtnRebuild = new QPushButton("重新生成排班", this);
    QPushButton *pBtnExport = new QPushButton("导出排班表", this);
    QPushButton *pBtnReturn = new QPushButton("返回上一页", this);
    QLabel *plab = new QLabel(QString("本月工作日数量为：%1天").arg(pDataInMonth->allWorkDay), myParent);
    plab->setFont( QFont("Microsoft YaHei", 15));

    pBtnRebuild->setStyleSheet(TABLEWIDGET_PUSHBUTTON_STYLE);
    pBtnExport->setStyleSheet(TABLEWIDGET_PUSHBUTTON_STYLE);
    pBtnReturn->setStyleSheet(TABLEWIDGET_PUSHBUTTON_STYLE);

    // 设置监听函数
    connect(pBtnRebuild, &QPushButton::clicked, this, &Schedule::slot_btnRebuild_clicked);
    connect(pBtnExport, &QPushButton::clicked, this, &Schedule::slot_btnExport_clicked);
    connect(pBtnReturn, &QPushButton::clicked, this, &Schedule::slot_btnReturn_clicked);

    // 设置布局
    if(NULL != btnLayout){
        deleteBoxLayout(btnLayout);
        delete btnLayout;
    }
    btnLayout = new QHBoxLayout;
    btnLayout->addStretch(1);
    btnLayout->addWidget(pBtnRebuild);
    btnLayout->addStretch(1);
    btnLayout->addWidget(plab);
    btnLayout->addStretch(3);
    btnLayout->addWidget(pBtnExport);
    btnLayout->addStretch(1);
    btnLayout->addWidget(pBtnReturn);
    btnLayout->addStretch(1);

    //设置总体布局
    if(NULL != widgetLayout) {
        deleteBoxLayout(widgetLayout);
        delete widgetLayout;
    }
    if(NULL != tableLayout && NULL != btnLayout && NULL == widgetLayout){
        widgetLayout = new QVBoxLayout;
        widgetLayout->addLayout(tableLayout);
        widgetLayout->addLayout(btnLayout);
        if(NULL == myParent->layout())
            myParent->setLayout(widgetLayout);
    }

    return;
}

void Schedule::fillData()
{
    // 创建模型
    calcScheduleData();

    // 排班
    startSchedule();

    // 画图
    paintTable();

    return;
}

void Schedule::calcScheduleData()
{
    // 初始化memberList和dayList
    fillPrivateList();

    // 计算成员相关数值
    calcMember();

    // 计算本月工作日数量 节假日数量 统一监控数量
    calcWorkTypeCount();

    // 计算DataInMonth中各种时间
    calcWorkTime();

    return;
}

void Schedule::fillPrivateList()
{
    for(int i = 0 ; i < MEMBERLIST.count(); i++){
        MemberClass* pTmp = new MemberClass;
        *pTmp = *MEMBERLIST.at(i);
        memberList.append(pTmp);
    }

    for(int i = 0 ; i < MYDAYLIST.count(); i++){
        myDay* pTmp = new myDay;
        *pTmp = *MYDAYLIST.at(i);
        dayList.append(pTmp);
    }

    return;
}

void Schedule::calcMember()
{
    MemberClass* pTmp = NULL;
    DataInMonth *pData = pDataInMonth;
    if(NULL == pData){
        QLOG_WARN() << "NULL == pData";
        return;
    }

    for(int i = 0; i < memberList.count(); i++){
        pTmp = memberList.at(i);
        if(NULL == pTmp){
            QLOG_DEBUG() << "memberList.at(" << i <<") == NULL";
            continue;
        }

        if(MEMBER_ABILITY_LEVEL_1 == pTmp->getMemberAbilityLevelIndex())
            pData->rookieCount++;
        else
            pData->commonCount++;
    }

    pData->allMemberCount = memberList.count();
    QLOG_INFO() << "本组成员共有" << pData->allMemberCount << "人，其中处于培训期的新人有" << pData->rookieCount << "人。";
    return;
}

void Schedule::calcWorkTypeCount()
{
    int month = 0;
    myDay* pTmp = NULL;
    DataInMonth *pData = pDataInMonth;
    if(NULL == pData){
        QLOG_WARN() << "NULL == pData";
        return;
    }

    for(int i = 0; i < dayList.count(); i++){
        pTmp = dayList.at(i);
        if(NULL == pTmp){
            QLOG_DEBUG() << "dayList.at(" << i <<") == NULL";
            continue;
        }

        month = pTmp->getMonth();
        switch (pTmp->GetDayType()){
        case DATESET_WORKDAY:
            pData->allWorkDay++;
            pData->workDayCount++;
            break;
        case DATESET_HOLIDAY:
            pData->holidayCount++;
            break;
        case DATESET_WORKDAY_AND_SPECIFYDAY:
            pData->allWorkDay++;
            pData->specifyDayCountInWorkDay++;
            break;
        case DATESET_HOLIDAY_AND_SPECIFYDAY:
            pData->holidayCount++;
            pData->specifyDayCountInHolidayDay++;
            break;
        default:
            break;
        }
    }

//    pData->specifyDayCount = pData->specifyDayCountInHolidayDay + pData->specifyDayCountInWorkDay;
    QLOG_INFO() << month << "月共有" << dayList.count() << "天，法定工作日共" << \
             pData->allWorkDay << "天，其中普通工作日" << pData->workDayCount << \
             "天，工作日统一监控" << pData->specifyDayCountInWorkDay << "天；节假日共" << \
             pData->holidayCount << "天，其中普通节假日" << pData->holidayCount  \
             - pData->specifyDayCountInHolidayDay << "天，节假日统一监控" << \
             pData->specifyDayCountInHolidayDay << "天。";

    return;
}

void Schedule::calcWorkTime()
{
    DataInMonth *pData = pDataInMonth;
    if(NULL == pData){
        QLOG_WARN() << "NULL == pData";
        return;
    }

    pData->allWorkTime = pData->allWorkDay * pData->commonCount;
//    pData->miniWorkDayRequireTime = (MINI_WORKDAY_DAY + MINI_WORKDAY_NIGHT * 2) * pData->workDayCount;
//    pData->miniHolidayRequireTime= (MINI_HOLIDAY_DAY + MINI_HOLIDAY_NIGHT * 2) * pData->holidayCount;
//    pData->miniSpecialDayRequireTime = (MINI_SPECIALDAY_DAY + MINI_SPECIALDAY_NIGHT * 2) * pData->specifyDayCount;
//    pData->miniRequireTime = pData->miniWorkDayRequireTime + pData->miniHolidayRequireTime + pData->miniSpecialDayRequireTime;

    return;
}

void Schedule::startSchedule()
{
    DataInMonth *pData = pDataInMonth;
    if(NULL == pData){
        QLOG_WARN() << "NULL == pData";
        return;
    }

    // 先根据dateset的右键菜单的设置需求，来修改各成员
    changeMemberWithDateAction();

    calcNeedWorkCount();

    allocateNightWork();

    allocateDayWork();

    // 新人跟着带教排班
    rookieSchedule();

    return;
}

void Schedule::calcNeedWorkCount(void)
{
    myDay* pDay = NULL;
    int dayNeedMemberCount = 0, nightNeedMemberCount = 0;  // 白、夜班次所需人员
    for(int i = 0; i < dayList.count(); i++){
        pDay = dayList.at(i);
        if(NULL == pDay){
            QLOG_DEBUG() << "NULL == pDay";
            continue;
        }

        // 普通工作日
        if(DATESET_WORKDAY == pDay->GetDayType()){
            dayNeedMemberCount = MINI_WORKDAY_DAY;
            nightNeedMemberCount = MINI_WORKDAY_NIGHT;
        }
        // 普通节假日
        else if(DATESET_HOLIDAY == pDay->GetDayType()){
            dayNeedMemberCount = MINI_HOLIDAY_DAY;
            nightNeedMemberCount = MINI_HOLIDAY_NIGHT;
        }
        // 统一监控
        else{
            dayNeedMemberCount = MINI_SPECIALDAY_DAY;
            nightNeedMemberCount = MINI_SPECIALDAY_NIGHT;
        }

        pDay->setNeedDayWorkCount(dayNeedMemberCount);
        pDay->setNeedNightWorkCount(nightNeedMemberCount);
    }

    return;
}

void Schedule::allocateDayWork()
{
    int curCount = 0;
    int expectMemberCount = 0;
    int lackMemberCount = 0;
    int dayCountSum = 0;
    int remainDay = 0; // 第二轮白班每天应该填充的人数
    int remainAllDayCount = 0;
    QHash <int, int> remainHash;        // key:记录第一轮排班未拍完的天数 value:记录第一轮排班未拍完的数量
    myDay* pDay = NULL;
    DataInMonth *pData = pDataInMonth;
    if(NULL == pData){
        QLOG_WARN() << "NULL == pData";
        return;
    }

    // 第一轮按照设定的人数排，休息间隔 MINI_NIGHTWORK_INTERVAL
    for(int i = 0; i < dayList.count(); i++){
        pDay = dayList.at(i);
        if(NULL == pDay){
            QLOG_DEBUG() << "NULL == pDay";
            continue;
        }

        curCount = pDay->getMemberList(DATESET_DAYWORK).count();
        expectMemberCount = pDay->getNeedDayWorkCount();
        lackMemberCount = expectMemberCount - curCount;

        if(lackMemberCount > 0){
            dayCountSum += lackMemberCount;
            allocateWorkToList(lackMemberCount, DATESET_DAYWORK, MINI_NIGHTWORK_INTERVAL, 1, pDay, true);
            // 把第一轮未排完的记录
            if(lackMemberCount > 0)
                remainHash.insert(i, lackMemberCount);
        }
    }


    remainAllDayCount = pData->allWorkTime - pData->NightCountSum * 2 - dayCountSum;
    remainDay = remainAllDayCount / dayList.count();

    // 第二轮多出的人数按照比例填充，休息间隔 MINI_NIGHTWORK_INTERVAL，不在限制白班数量
    // 按照比例分摊到每一天
    for(int i = 0; i < dayList.count(); i++){
        pDay = dayList.at(i);
        if(NULL == pDay){
            QLOG_DEBUG() << "NULL == pDay";
            continue;
        }

        lackMemberCount = 0;
        if(true == remainHash.contains(i))
            lackMemberCount += remainHash.value(i);

        if(DATESET_WORKDAY == pDay->GetDayType() || DATESET_HOLIDAY == pDay->GetDayType())
            lackMemberCount += remainDay;
        else
            lackMemberCount += remainDay + 1;

        if(lackMemberCount > 0)
            allocateWorkToList(lackMemberCount, DATESET_DAYWORK, MINI_NIGHTWORK_INTERVAL, 2, pDay, false);
    }

    return;
}

void Schedule::rookieSchedule()
{
    MemberClass* pMember = NULL;
    MemberClass* pMemberTeacher = NULL;
    myDay* pDay = NULL;
    QVector<int> dayVec;
    QVector<int> nightVec;

    for(int i = 0; i < memberList.count(); i++){
        pMember = memberList[i];
        if(NULL == pMember)      continue;

        // 跟随带教
        if(MEMBER_ABILITY_LEVEL_1 == pMember->getMemberAbilityLevelIndex() && "" != pMember->getTeacherName()){
            for(int j = 0; j < memberList.count(); j++){
                pMemberTeacher = memberList[j];
                if(NULL == pMemberTeacher)      continue;
                if(pMember->getTeacherName() == pMemberTeacher->getMemberName())
                    break;
            }

            if(NULL == pMemberTeacher)      continue;
            dayVec = pMemberTeacher->getDayVec();
            nightVec = pMemberTeacher->getNightVec();
            pMember->setDayCount(pMemberTeacher->getDayCount());
            pMember->setNightCount(pMemberTeacher->getNightCount());
            pMember->setDayVec(dayVec);
            pMember->setNightVec(nightVec);

            for(int j = 0; j < dayVec.count(); j++){
                pDay = dayList[dayVec.at(j)];
                if(NULL == pDay)      continue;

                pDay->stringlistAppend(DATESET_DAYWORK, pMember->getMemberName());
            }

            for(int j = 0; j < nightVec.count(); j++){
                pDay = dayList[nightVec.at(j)];
                if(NULL == pDay)      continue;

                pDay->stringlistAppend(DATESET_NIGHTWORK, pMember->getMemberName());
            }
        }
    }

    return;
}

void Schedule::allocateNightWork(void)
{
    int curCount = 0;
    int expectMemberCount = 0;
    int lackMemberCount = 0;
    myDay* pDay = NULL;
    DataInMonth *pData = pDataInMonth;
    if(NULL == pData){
        QLOG_WARN() << "NULL == pData";
        return;
    }

    // 第一轮排夜班，以 DEFAULT_NIGHTWORK_INTERVAL 为班次之间间隔
    for(int i = 0; i < dayList.count(); i++){
        pDay = dayList.at(i);
        if(NULL == pDay){
            QLOG_DEBUG() << "NULL == pDay";
            continue;
        }

        curCount = pDay->getMemberList(DATESET_NIGHTWORK).count();
        expectMemberCount = pDay->getNeedNightWorkCount();
        lackMemberCount = expectMemberCount - curCount;

        if(lackMemberCount > 0)
            allocateWorkToList(lackMemberCount, DATESET_NIGHTWORK, DEFAULT_NIGHTWORK_INTERVAL, 1, pDay, true);
    }

    // 第二轮填充夜班，以 MINI_NIGHTWORK_INTERVAL 为班次之间间隔
    for(int i = 0; i < dayList.count(); i++){
        pDay = dayList.at(i);
        if(NULL == pDay){
            QLOG_DEBUG() << "NULL == pDay";
            continue;
        }

        curCount = pDay->getMemberList(DATESET_NIGHTWORK).count();
        expectMemberCount = pDay->getNeedNightWorkCount();
        lackMemberCount = expectMemberCount - curCount;

        if(lackMemberCount > 0)
            allocateWorkToList(lackMemberCount, DATESET_NIGHTWORK, MINI_NIGHTWORK_INTERVAL, 2, pDay, true);
    }

    return;
}

void Schedule::allocateWorkToList(int & count, int type, int interval, int loop, myDay* & pDay, bool expectflag)
{
    int ret = 0;
    int index = 0;
    QVector<int> errorvec;  // 记录验证失败的index
    MemberClass * pMember = NULL;
    QString prefix = QString("%1月%2日第%3轮").arg(pDay->getMonth()).arg(pDay->GetDayOfMonth() + 1).arg(loop);


    if(DATESET_DAYWORK == type){
        QLOG_INFO() << "开始进行" << prefix.toUtf8().data() \
                    << "白班排班，需要" << pDay->getNeedDayWorkCount() << "人" << "，实际：" \
                    << pDay->getMemberList(type).count() << "人。";
    }else{
        QLOG_INFO() << "开始进行" << prefix.toUtf8().data() \
                    << "夜班排班，需要" << pDay->getNeedNightWorkCount() << "人" << "，实际：" \
                    << pDay->getMemberList(type).count() << "人。";
    }

    errorvec.clear();
    while(0 < count){
        index = randSelectMember(errorvec);
        if(index < 0)    break;

        pMember = memberList[index];

        // 验证并添加当天的班次，验证不通过便重新挑选
        ret = VerifyWorkToList(pMember, pDay, type, interval, expectflag);
        if(-1 == ret){
            QLOG_DEBUG() << pMember->getMemberName() << "验证失败";
            errorvec.append(index);
            continue;
        }

        // 修改pMember pDay
        addWorkToList(pMember, pDay, type);
        count--;

        // errorvec添加index，等再次randSelectMember时就不会返回这个index了
        errorvec.append(index);
    }

    if(DATESET_DAYWORK == type){
        QLOG_INFO() << "完成" << prefix.toUtf8().data() \
                    << "白班排班，需要" << pDay->getNeedDayWorkCount() << "人" << "，实际：" \
                    << pDay->getMemberList(type).count() << "人。";
    }else{
        QLOG_INFO() << "完成" << prefix.toUtf8().data() \
                    << "夜班排班，需要" << pDay->getNeedNightWorkCount() << "人" << "，实际：" \
                    << pDay->getMemberList(type).count() << "人。";
    }

    return;
}

int Schedule::randSelectMember(QVector<int> & errorvec)
{
    int commonCount = pDataInMonth->commonCount;
    int index = -1;
    MemberClass* pMember = NULL;

    while(errorvec.count() < commonCount){
        index = QRandomGenerator::global()->bounded(memberList.count());
        // 本次随机的人选在errorvec内，说明这个人已经尝试过但未通过
        if(true == errorvec.contains(index))    {
            QLOG_DEBUG() << "index=" << index << "已被使用或验证不通过";
            continue;
        }

        pMember = memberList.at(index);
        if(NULL == pMember){
            QLOG_DEBUG() << "index=" << index << "成员不存在";
            continue;
        }

        QLOG_DEBUG() << "使用随机，index =" << index << "name = " <<pMember->getMemberName() ;
        return index;
    }

    QLOG_DEBUG() << "errorvec数量已满，无人可用";
    return -1;
}

int Schedule::checkWorkInterval(const QVector<int> &vec, int day, int lowerlimit, int type = DATESET_DAYWORK)
{
    // 用lowerlimit来控制班次之间间隔
    for(int j = 0; j <= lowerlimit; j++){
       // 前 lowerlimit 有班，不在考虑范围
       if(true == vec.contains(day - j))
            return -1;
    }

    if(DATESET_NIGHTWORK == type){
        if(true == vec.contains(day + 1) || true == vec.contains(day + 2))
             return -1;
    }

    // 前 lowerlimit 没有班，直接返回
    return 0;
}

int Schedule::checkNightCountAverage(int count)
{
    if(0 == pDataInMonth->commonCount){
        QLOG_WARN() << "当前非新人数量为0，无法排班。";
        return -1;
    }

    int avg = floor((double)pDataInMonth->NightCountSum/pDataInMonth->commonCount);
    QLOG_DEBUG() << "当前夜班数量" << count << "，夜班平均值：" << avg;
    if(count > avg)
        return -1;

    return 0;
}

int Schedule::addWorkToList(MemberClass *&pMember, myDay *&pDay, int type)
{
    int dayofmonth = pDay->GetDayOfMonth();

    // 修改pMember
    pMember->vectorAppend(type, dayofmonth);
    pMember->workCountPlus(type, 1);

    // 修改 pDay
    pDay->stringlistAppend(type, pMember->getMemberName());

    if(DATESET_DAYWORK == type){
        QLOG_INFO() << "白班添加人员：" << pMember->getMemberName() \
                    << "，添加后已有白班数量：" <<  pMember->getDayCount() ;
    }else{
        pDataInMonth->NightCountSum++;
        QLOG_INFO() << "夜班添加人员：" << pMember->getMemberName() \
                    << "，添加后已有夜班数量：" <<  pMember->getNightCount() \
                    << "，当前夜班总数：" << pDataInMonth->NightCountSum;
    }

    return 0;
}

/*
当VerifyWorkToList验证白班时，把nightInterval传入 MINI_NIGHTWORK_INTERVAL - 1
当VerifyWorkToList验证夜班时，把nightInterval传入 DEFAULT_NIGHTWORK_INTERVAL
*/
int Schedule::VerifyWorkToList(MemberClass *&pMember, myDay *&pDay, int type, int nightInterval, bool expectflag = true)
{
    QVector<int> dayvec;
    QVector<int> nightvec;
    QStringList daynoworklist;
    QStringList nightnoworklist;
    int dayofmonth = pDay->GetDayOfMonth();

    // 对pMember的验证，如果不合格重新生成
    // 是新人，重新选择
    if(0 == ALLOW_ROOKIE_FLAG && MEMBER_ABILITY_LEVEL_1 == pMember->getMemberAbilityLevelIndex()){
        QLOG_DEBUG() << "是新人";
        return -1;
    }

    dayvec = pMember->getDayVec();
    nightvec = pMember->getNightVec();

    // 同一天白班 或者同一天夜班
    if(true == dayvec.contains(dayofmonth) || true == nightvec.contains(dayofmonth)){
        QLOG_DEBUG() << "同一天白班 或者同一天夜班";
        return -1;
    }

    if(checkWorkInterval(nightvec, dayofmonth, nightInterval, type) < 0){
        QLOG_DEBUG() << "休息时长不满足";
        return -1;
    }

    // 白班
    if(DATESET_DAYWORK == type){
        // 班次总数>法定工作数
        if(pMember->getWorkCount() >= pDataInMonth->allWorkDay){
            QLOG_DEBUG() << "班次总数超过本月法定工作数";
            return -1;
        }

        // 人员数量超过期望值
        // expectflag为true，验证白班人数 否则不验证
        if(true == expectflag && pDay->getNeedDayWorkCount() <= pDay->getMemberList(DATESET_DAYWORK).count()){
            QLOG_DEBUG() << "人员数量超过期望值";
            return -1;
        }

        // 包含在今日不上白班链表内
        daynoworklist = pDay->getMemberList(DATESET_NODAYWORK);
        if(true == daynoworklist.contains(pMember->getMemberName())){
            QLOG_DEBUG() << "包含在今日不上白班链表内";
            return -1;
        }
    }
    // 夜班
    else{
        // 夜班数量超过平均值
        if(checkNightCountAverage(nightvec.count()) < 0){
            QLOG_DEBUG() << "当前夜班数量已超过平均值";
            return -1;
        }

        // 夜班数量超过最大值
        if(nightvec.count() >= MAXI_NIGHT_COUNT){
            QLOG_DEBUG() << "当前夜班数量已超过平均值";
            return -1;
        }

        // 人员数量超过期望值
        if(pDay->getNeedNightWorkCount() <= pDay->getMemberList(DATESET_NIGHTWORK).count()){
            QLOG_DEBUG() << "人员数量超过期望值";
            return -1;
        }

        // 包含在今日不上夜班链表内
        nightnoworklist = pDay->getMemberList(DATESET_NONIGHTWORK);
        if(true == nightnoworklist.contains(pMember->getMemberName())){
            QLOG_DEBUG() << "包含在今日不上夜班链表内";
            return -1;
        }

        // 明天夜班/白班
        if(true == nightvec.contains(dayofmonth + 1) || true == dayvec.contains(dayofmonth + 1)){
            QLOG_DEBUG() << "明天夜班/白班";
            return -1;
        }
    }

    QLOG_DEBUG() << pMember->getMemberName() << "验证通过";
    return 0;
}

void Schedule::changeMemberWithDateAction()
{
    myDay* pDate = NULL;
    MemberClass* pMember = NULL;
    QStringList nameDayWorkList;
    QStringList nameNightWorkList;

    for(int i = 0; i < dayList.count(); i++){
        pDate = dayList.at(i);
        if(NULL == pDate){
            QLOG_DEBUG() << "NULL == pDate";
            continue;
        }

        nameDayWorkList = pDate->getMemberList(DATESET_DAYWORK);
        nameNightWorkList = pDate->getMemberList(DATESET_NIGHTWORK);

        for(int j = 0; j < nameDayWorkList.count(); j++){
            for(int k = 0; k < memberList.count(); k++){
                pMember = memberList.at(k);
                if(NULL == pMember){
                    QLOG_DEBUG() << "NULL == pMember";
                    continue;
                }

                if(pMember->getMemberName() == nameDayWorkList.at(j)){
                    pMember->vectorAppend(DATESET_DAYWORK, i);
                    pMember->setDayCount(pMember->getDayCount() + 1);
                    break;
                }
            }
        }

        for(int j = 0; j < nameNightWorkList.count(); j++){
            for(int k = 0; k < memberList.count(); k++){
                pMember = memberList.at(k);
                if(NULL == pMember){
                    QLOG_DEBUG() << "NULL == pMember";
                    continue;
                }

                if(pMember->getMemberName() == nameNightWorkList.at(j)){
                    pMember->vectorAppend(DATESET_NIGHTWORK, i);
                    pMember->setNightCount(pMember->getNightCount() + 1);
                    break;
                }
            }
        }
    }

    return;
}


void Schedule::paintTable()
{
    // 画中央表格 指排班部分表格
    painteCentralTable();

    // 画特殊行  个人白班小计、个人夜班小计、个人班时小计
    painteSpecialRow();

    // 画特殊列 白班人数、夜班人数、总人数
    painteSpecialCol();

    return;
}

void Schedule::painteCentralTable()
{
    MemberClass* pMember = NULL;
    for(int i = 0; i < memberList.count(); i++){
        pMember = memberList.at(i);
        if(NULL == pMember){
            QLOG_DEBUG() << "NULL == pMember";
            continue;
        }

        paintDayAndNight(pMember->getDayVec(), pMember->getNightVec(), i);
    }

    return ;
}

void Schedule::paintDayAndNight(const QVector<int> & vec0, const QVector<int> & vec1, int col)
{
    for(int i = 0; i < vec0.count(); i++)
        pTable->paintScheduleItem(vec0.at(i), col, "白", SCHEDULE_TABLE_DAY_ITEM_COLOR);

    for(int i = 0; i < vec1.count(); i++)
        pTable->paintScheduleItem(vec1.at(i), col, "夜", SCHEDULE_TABLE_NIGHT_ITEM_COLOR);

    return;
}

void Schedule::painteSpecialRow()
{
    MemberClass* pMember = NULL;

    for(int i = 0; i < memberList.count(); i++){
        pMember = memberList.at(i);
        if(NULL == pMember){
            QLOG_DEBUG() << "NULL == pMember";
            continue;
        }

        pTable->paintScheduleItem(specialRowStart, i, QString::number(pMember->getDayCount()), SCHEDULE_TABLE_SPECIFY_ITEM_COLOR);
        pTable->paintScheduleItem(specialRowStart + 1, i, QString::number(pMember->getNightCount()), SCHEDULE_TABLE_SPECIFY_ITEM_COLOR);
        pTable->paintScheduleItem(specialRowStart + 2, i, QString::number(pMember->getDayCount() + pMember->getNightCount() * 2), SCHEDULE_TABLE_SPECIFY_ITEM_COLOR);
    }

    return;
}

void Schedule::painteSpecialCol()
{
    myDay* pDate = NULL;
    QColor color;

    for(int i = 0; i < dayList.count(); i++){
        pDate = dayList.at(i);
        if(NULL == pDate){
            QLOG_DEBUG() << "NULL == pMember";
            continue;
        }

        pTable->paintScheduleItem(i, specialColumnStart, QString::number(pDate->getMemberList(DATESET_DAYWORK).count()), SCHEDULE_TABLE_SPECIFY_ITEM_COLOR);
        pTable->paintScheduleItem(i, specialColumnStart + 1, QString::number(pDate->getMemberList(DATESET_NIGHTWORK).count()), SCHEDULE_TABLE_SPECIFY_ITEM_COLOR);
        pTable->paintScheduleItem(i, specialColumnStart + 2, QString::number(pDate->getMemberList(DATESET_DAYWORK).count() + pDate->getMemberList(DATESET_NIGHTWORK).count()), SCHEDULE_TABLE_SPECIFY_ITEM_COLOR);

        // 画备注列单元格，根据daytype决定颜色
        if(DATESET_HOLIDAY == pDate->GetDayType())
            color = DATESET_TABLE_HOLIDAY_COLOR;
        else if(DATESET_WORKDAY_AND_SPECIFYDAY == pDate->GetDayType() || DATESET_HOLIDAY_AND_SPECIFYDAY  == pDate->GetDayType())
            color = DATESET_TABLE_SPECIFYDAY_COLOR;
        else
            color = DATESET_TABLE_WORKDAY_COLOR;

        pTable->paintScheduleItem(i, specialColumnStart + 3, "", color);
    }

    return;
}

void Schedule::changeActionScheduleList(int row, int col)
{
    int daycount = 0, nightcount = 0;

    QTableWidgetItem *pItem = NULL;
    QTableWidgetItem *pItem2 = NULL;
    QTableWidgetItem *pItem3 = NULL;

    for(int i = 0; i < dayList.count(); i++){
        pItem = pTable->item(i, col);
        if(NULL == pItem || "" == pItem->text())    continue;

        if("白" == pItem->text())    daycount++;
        else if("夜" == pItem->text())   nightcount++;
    }

    pItem = pTable->item(dayList.count() , col);
    pItem2 = pTable->item(dayList.count() + 1, col);
    pItem3 = pTable->item(dayList.count() + 2, col);
    if(NULL == pItem || NULL == pItem2 || NULL == pItem3){
        QLOG_WARN() << "NULL == pItem || NULL == pItem2 || NULL == pItem3" << pItem << pItem2 << pItem3;
        return;
    }

    pItem->setText(QString::number(daycount));
    pItem2->setText(QString::number(nightcount));
    pItem3->setText(QString::number(daycount + nightcount * 2));


    daycount = nightcount = 0;
    for(int i = 0; i <memberList.count(); i++ ){
        pItem = pTable->item(row, i);
        if(NULL == pItem || "" == pItem->text())    continue;

        if("白" == pItem->text())    daycount++;
        else if("夜" == pItem->text())   nightcount++;
    }

    pItem = pTable->item(row ,memberList.count());
    pItem2 = pTable->item(row,memberList.count() + 1);
    pItem3 = pTable->item(row,memberList.count() + 2);
    if(NULL == pItem || NULL == pItem2 || NULL == pItem3){
        QLOG_WARN() << "NULL == pItem || NULL == pItem2 || NULL == pItem3" << pItem << pItem2 << pItem3;
        return;
    }

    pItem->setText(QString::number(daycount));
    pItem2->setText(QString::number(nightcount));
    pItem3->setText(QString::number(daycount + nightcount));

    return;
}

void Schedule::slot_Schedule_start()
{
    initScheduleTable();

    fillData();

    initSchedulePushButton();

    return;
}

void Schedule::slot_btnRebuild_clicked()
{
    this->clearData();
    this->slot_Schedule_start();
    return;
}

void Schedule::slot_btnExport_clicked()
{
    emit this->signal_exportTableBtn_triggered(this->memberList, this->dayList);
    return;
}

void Schedule::slot_btnReturn_clicked()
{
    pStackWidget->setCurrentIndex(STACKWIDGET_DATE_INDEX);
    return;
}

void Schedule::slot_changeScheduleWorkAction_triggered(QPoint pos, int type)
{
    int row = 0, col = 0;
    QTableWidgetItem * pItem = NULL;

    pItem =pTable->getItemRowAndColFromPos(pos, row, col);
    if( NULL == pItem){
        QLOG_WARN() << "getItemRowAndColFromPos err";
        return;
    }

    // 改变相关单元格的背景颜色及内容
    pTable->changeScheduleItem(pItem, type);

    // 改变指定list
    changeActionScheduleList(row, col);

    return;
}



template<class Type>
void Schedule::deleteBoxLayout(Type *t)
{
    if(NULL == t)
        return;

    QLayoutItem* child;
    while((child = t->takeAt(0)) != 0){
        if(NULL == child->widget())
            continue;
        //必须将parent置为nullpptr，否则不消失
        t->removeWidget(child->widget());
        child->widget()->setParent(NULL);
        delete child;
    }

    return;
}
